﻿using System;
using System.Collections.Generic;
using System.Xml.XPath;

namespace Songhay.Data.GenericWeb
{
    using Songhay.Data.GenericWeb.DataTransferContracts;
    using Songhay.Data.GenericWeb.InputDefinitions;
    using Songhay.Data.GenericWeb.OutputDefinitions;
    using Songhay.Data.GenericWeb.Procedures;
    using Songhay.Xml;

    /// <summary>
    /// The façade/mediator that handles data commands from external layers.
    /// </summary>
    public partial class CommandHandler : ICommandHandler
    {
        #region ICommandHandler Members

        /// <summary>
        /// Returns a <see cref="System.Xml.XPath.XPathDocument"/>
        /// based on the specified incoming message.
        /// </summary>
        /// <param name="input">The specified incoming message with POST data</param>
        public XPathDocument DoCommand<MessageType>(DataCommandInput<MessageType> input)
        {
            this._command = DataCommandInput.GetDataCommand<DataCommand>(input.CommandName);

            string inputUniqueId = input.GetUniqueIdentifier();
            IXPathNavigable inputMessage = (string.IsNullOrEmpty(inputUniqueId)) ? XmlUtility.InputAs<MessageType>(input.Message) : null;
            XPathDocument outputDocument = null;
            switch(this._command)
            {
                case DataCommand.DeleteDocument:
                    IDocument inputDeleteDocument = InputDocument.InputDelete(inputMessage);
                    int? inputDeleteDocumentId = Delete.Document(input.DataConnection, inputDeleteDocument);
                    outputDocument = XPathNavigableOutput.GetDeleteMessage(inputDeleteDocumentId, "Document");
                    break;

                case DataCommand.DeleteFragment:
                    IFragment inputDeleteFragment = InputFragment.InputDelete(inputMessage);
                    int? inputDeleteFragmentId = Delete.Fragment(input.DataConnection, inputDeleteFragment);
                    outputDocument = XPathNavigableOutput.GetDeleteMessage(inputDeleteFragmentId, "Fragment");
                    break;

                case DataCommand.DeleteSegment:
                    ISegment inputDeleteSegment = InputSegment.InputDelete(inputMessage);
                    int? inputDeleteSegmentId = Delete.Segment(input.DataConnection, inputDeleteSegment);
                    outputDocument = XPathNavigableOutput.GetDeleteMessage(inputDeleteSegmentId, "Segment");
                    break;

                case DataCommand.GetDocumentForEdit:
                    outputDocument = XPathNavigableOutput.EditDocument(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.GetFragmentForEdit:
                    outputDocument = XPathNavigableOutput.EditFragment(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.GetSegmentForEdit:
                    outputDocument = XPathNavigableOutput.EditSegment(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.InsertDocument:
                    IDocument inputInsertDocument = InputDocument.InputInsert(inputMessage);
                    List<ISegmentDocument> inputInsertDocumentSegments = InputSegmentDocument.GetList(inputMessage);
                    List<IWebKeyword> inputInsertWebKeywords = InputWebKeyword.GetList(inputMessage);
                    int? inputInsertDocumentId = Insert.Document(input.DataConnection, inputInsertDocument, inputInsertDocumentSegments, inputInsertWebKeywords);

                    inputUniqueId = FrameworkType.ParseString(inputInsertDocumentId);
                    outputDocument = XPathNavigableOutput.EditDocument(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.InsertFragment:
                    IFragment inputInsertFragment = InputFragment.InputInsert(inputMessage);
                    int? inputInsertFragmentId = Insert.Fragment(input.DataConnection, inputInsertFragment);

                    inputUniqueId = FrameworkType.ParseString(inputInsertFragmentId);
                    outputDocument = XPathNavigableOutput.EditFragment(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.InsertSegment:
                    ISegment inputInsertSegment = InputSegment.InputInsert(inputMessage);
                    int? inputInsertSegmentId = Insert.Segment(input.DataConnection, inputInsertSegment);

                    inputUniqueId = FrameworkType.ParseString(inputInsertSegmentId);
                    outputDocument = XPathNavigableOutput.EditSegment(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.Search:
                    outputDocument = XPathNavigableOutput.GetSearchResults(input.DataConnection, inputMessage);
                    break;

                case DataCommand.SetDocument:
                    IDocument inputUpdateDocument = InputDocument.InputUpdate(inputMessage);
                    List<ISegmentDocument> inputUpdateDocumentSegments = InputSegmentDocument.GetList(inputMessage);
                    List<IWebKeyword> inputUpdateWebKeywords = InputWebKeyword.GetList(inputMessage);
                    int? inputUpdateDocumentId = Update.Document(input.DataConnection, inputUpdateDocument, inputUpdateDocumentSegments, inputUpdateWebKeywords);

                    inputUniqueId = FrameworkType.ParseString(inputUpdateDocumentId);
                    outputDocument = XPathNavigableOutput.EditDocument(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.SetFragment:
                    IFragment inputUpdateFragment = InputFragment.InputUpdate(inputMessage);
                    int? inputUpdateFragmentId = Update.Fragment(input.DataConnection, inputUpdateFragment);

                    inputUniqueId = FrameworkType.ParseString(inputUpdateFragmentId);
                    outputDocument = XPathNavigableOutput.EditFragment(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.SetSegment:
                    ISegment inputUpdateSegment = InputSegment.InputUpdate(inputMessage);
                    int? inputUpdateSegmentId = Update.Segment(input.DataConnection, inputUpdateSegment);

                    inputUniqueId = FrameworkType.ParseString(inputUpdateSegmentId);
                    outputDocument = XPathNavigableOutput.EditSegment(input.DataConnection, inputUniqueId);
                    break;

                case DataCommand.Undefined:
                    outputDocument = DataCommandInput.GetUndefinedCommandMessage(input.CommandName);
                    break;

                default:
                    outputDocument = DataCommandInput.GetNotImplementedMessage(input.CommandName);
                    break;
            }

            if(outputDocument == null) outputDocument = DataCommandInput.GetInputNotHandledMessage();

            return outputDocument;
        }

        /// <summary>
        /// Returns a <see cref="System.Xml.XPath.XPathDocument"/>
        /// based on the specified incoming message.
        /// </summary>
        /// <param name="input">The specified incoming message</param>
        public XPathDocument DoCommand(DataCommandInput input)
        {
            this._command = DataCommandInput.GetDataCommand<DataCommand>(input.CommandName);

            XPathDocument returnDocument = null;

            switch(this._command)
            {
                case DataCommand.ListGenericWebIndexForEdit:
                    returnDocument = XPathNavigableOutput.GetSegmentDocumentTree(input.DataConnection);
                    break;

                case DataCommand.NewDocument:
                    returnDocument = XPathNavigableOutput.NewDocument(input.DataConnection);
                    break;

                case DataCommand.NewFragment:
                    returnDocument = XPathNavigableOutput.NewFragment(input.DataConnection);
                    break;

                case DataCommand.NewSegment:
                    returnDocument = XPathNavigableOutput.NewSegment(input.DataConnection);
                    break;

                case DataCommand.Undefined:
                    returnDocument = DataCommandInput.GetUndefinedCommandMessage(input.CommandName);
                    break;

                default:
                    //CONVENTION: “Chain” to strongly typed member, looking for any other data commands:
                    DataCommandInput<string> inputAsString = new DataCommandInput<string>(input.CommandName, input.DataConnection);
                    returnDocument = this.DoCommand<string>(inputAsString);
                    break;
            }

            return returnDocument;
        }

        /// <summary>
        /// Reads the <see cref="Songhay.Data.DataAccessItem"/>
        /// of the <see cref="Songhay.Data.DataAccessConfiguration"/>
        /// corresponding to the context of the implementor.
        /// </summary>
        /// <param name="item">the specified <see cref="Songhay.Data.DataAccessItem"/></param>
        public void ReadDataAccessItem(DataAccessItem item)
        {
            CommandHandler._globalDateFormat = item.GetNameValuePairValue("GlobalDateFormat");
            CommandHandler._isManySegmentsToManyDocuments = string.Equals(item.GetNameValuePairValue("IsManySegmentsToManyDocuments"), "yes", StringComparison.CurrentCultureIgnoreCase);
        }

        #endregion

        internal static string GlobalDateFormat
        {
            get { return CommandHandler._globalDateFormat; }
        }

        internal static bool IsManySegmentsToManyDocuments
        {
            get { return CommandHandler._isManySegmentsToManyDocuments; }
        }

        static bool _isManySegmentsToManyDocuments;
        static string _globalDateFormat;

        DataCommand _command;
    }
}
